package com.example.origin.aspect.config;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.example.origin.aspect.annotation.Log;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StopWatch;

import java.util.Map;
import java.util.TreeMap;

/**
 * @author yutao.zhang
 * @create 2023-07-25 17:09
 **/
@Slf4j
@Aspect
@Configuration
public class LogAspect {

    @Pointcut("execution(public * com.example.*..controller.*.*(..))")
    public void anyController() {
    }

    @SneakyThrows
    @Around("anyController()")
    public Object around(ProceedingJoinPoint point) {
        Log lg = ((MethodSignature) point.getSignature()).getMethod().getAnnotation(Log.class);
        if (lg != null && !lg.value()) {
            return point.proceed();
        }
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        String className = "";
        String methodName = "";
        String paramStr = "";
        String resultStr = "";
        try {

            Map<String, Object> paramMap = new TreeMap<>();
            Object[] values = point.getArgs();
            String[] names = ((CodeSignature) point.getSignature()).getParameterNames();
            for (int i = 0; i < names.length; i++) {
                paramMap.put(names[i], values[i]);
            }
            if (ObjectUtil.isNotEmpty(paramMap)) {
                paramStr = JSONUtil.toJsonStr(paramMap);
            }

            className = point.getTarget().getClass().getName();

            methodName = point.getSignature().getName();

            Object result = point.proceed();

            if (result != null) {
                resultStr = JSONUtil.toJsonStr(result);
            }

            return result;
        } catch (Throwable throwable) {
            resultStr = throwable.getMessage();
            throw throwable;
        } finally {
            stopWatch.stop();
            long totalTimeMillis = stopWatch.getTotalTimeMillis();
            if (ObjectUtil.isNotEmpty(paramStr)) {
                paramStr = ":" + paramStr;
            }
            if (ObjectUtil.isNotEmpty(resultStr)) {
                resultStr = "->" + resultStr;
            }
            log.info("{}ms|{}#{}{}{}", totalTimeMillis, className, methodName, paramStr, resultStr);
        }


    }


}
